/*
 * $Id: InetSocket.cc,v 1.17 2005-12-25 07:11:46 bacon Exp $
 */

#include <xpp/net/InetSocket.h>
#include <xp/bas/locale.h>
#include <xp/bas/string.h>
#include <xp/bas/sysapi.h>

#ifndef _WIN32
	#include <net/if.h>
	#include <net/if_arp.h>
	#include <sys/ioctl.h>
#endif

namespace xpp
{
	namespace net
	{

		InetSocket::InetSocket (int type): Socket (type)
		{
		}

		int InetSocket::open (Handle handle)
		{
			return Socket::open (handle);
		}

		int InetSocket::open (Handle handle, const Address& target)
		{
			if (Socket::open(handle) == -1) return -1;
			this->target = (const InetAddress&)target;
			return 0;
		}

		int InetSocket::open (const Address& target)
		{
			XPP_NET_SOCKET_CHECK_OPEN_HANDLE (this->sckhnd);

			if (create_socket (target.family(), 
				this->socket_type, 0) == -1) return -1;
			this->target = (const InetAddress&)target;
			return 0;	
		}

		int InetSocket::bind ()
		{
			XPP_NET_SOCKET_CHECK_CLOSED_HANDLE (this->sckhnd);
			return bind_socket (
				this->target.storage(), 
				this->target.storageSize());
		}

		int InetSocket::bind (const Address& bind_target)
		{
			XPP_NET_SOCKET_CHECK_CLOSED_HANDLE (this->sckhnd);
			return bind_socket (
				bind_target.storage(), 
				bind_target.storageSize());
		}

		int InetSocket::getAddress (InetAddress* address)
		{
			InetAddress tmp (InetAddress::V0, this->socket_type);
			xp_scklen_t len = tmp.storageSize();

			if (xp_sck_getself (
				this, tmp.storage(), &len) == -1) return -1;
			*address = tmp;
			return 0;
		}

		int InetSocket::getPeerAddress (InetAddress* address)
		{
			InetAddress tmp (InetAddress::V0, this->socket_type);
			xp_scklen_t len = tmp.storageSize();

			if (xp_sck_getpeer(
				this, tmp.storage(), &len) == -1) return -1;
			*address = tmp;
			return 0;
		}

#ifndef _WIN32
		int InetSocket::getIfceIndex (const xp_char_t* name)
		{
		#ifdef SIOCGIFINDEX
			struct ifreq ifr;
		
			xp_memset (&ifr, 0, xp_sizeof(ifr));		
		#ifdef XP_CHAR_IS_MCHAR
			xp_strxcpy (ifr.ifr_name, xp_countof(ifr.ifr_name), name);
		#else
			if (xp_wcstomcs(name, ifr.ifr_name,
				xp_countof(ifr.ifr_name)) < xp_strlen(name)) return -1;
		#endif
			if (ioctl(this->sckhnd, SIOCGIFINDEX, &ifr) == -1) return -1;

			#ifdef ifr_ifindex
			return ifr.ifr_ifindex;
			#else
			return ifr.ifr_index;
			#endif
		#else
			return -1;
		#endif
		}

		int InetSocket::getIfceName (int index, xp_char_t* buf, xp_size_t size)
		{
		#ifdef SIOCGIFNAME
			struct ifreq ifr;
			
			xp_memset (&ifr, 0, xp_sizeof(ifr));
			#ifdef ifr_ifindex
			ifr.ifr_ifindex = index;
			#else
			return ifr.ifr_index;
			#endif

			if (ioctl(this->sckhnd, SIOCGIFNAME, &ifr) == -1) return -1;
		#ifdef XP_CHAR_IS_MCHAR
			xp_strxcpy (buf, size, ifr.ifr_name);
		#else
			xp_mcstowcs (ifr.ifr_name, buf, size);
		#endif
			return 0;
		#else
			return -1;
		#endif
		}

		int InetSocket::getIfceEtherAddress (
			const xp_char_t* name, EtherAddress* addr)
		{
		#if defined(SIOCGIFHWADDR) || defined(SIOCGIFPHYADDR)
			struct ifreq ifr;
		
			xp_memset (&ifr, 0, xp_sizeof(ifr));		
		#ifdef XP_CHAR_IS_MCHAR
			xp_strxcpy (ifr.ifr_name, xp_countof(ifr.ifr_name), name);
		#else
			if (xp_wcstomcs(name, ifr.ifr_name,
				xp_countof(ifr.ifr_name)) < xp_strlen(name)) return -1;
		#endif

		#ifdef SIOCGIFHWADDR
			if (ioctl(this->sckhnd, SIOCGIFHWADDR, &ifr) == -1) return -1;
		#else
			if (ioctl(this->sckhnd, SIOCGIFPHYADDR, &ifr) == -1) return -1;
		#endif
			
			int f = ifr.ifr_hwaddr.sa_family;
			if (f != ARPHRD_ETHER && f != ARPHRD_EETHER &&
			    f != ARPHRD_NETROM && f != ARPHRD_PPP &&
			    f != ARPHRD_IEEE802) return -1;

			*addr = (xp_byte_t*)&ifr.ifr_addr.sa_data;
			return 0;
		#else
			return -1;
		#endif
		}

		int InetSocket::getIfceInetAddress (
			const xp_char_t* name, InetAddress* addr)
		{
		#ifdef SIOCGIFADDR
			return get_ifce_address (name, SIOCGIFADDR, addr);
		#else
			return -1;
		#endif
		}

		int InetSocket::getIfceNetmask (
			const xp_char_t* name, InetAddress* addr)
		{
		#ifdef SIOCGIFNETMASK
			return get_ifce_address (name, SIOCGIFNETMASK, addr);
		#else
			return 1;
		#endif
		}

		int InetSocket::getIfceBroadcast (
			const xp_char_t* name, InetAddress* addr)
		{
		#ifdef SIOCGIFBRDADDR
			return get_ifce_address (name, SIOCGIFBRDADDR, addr);
		#else
			return -1;
		#endif
		}
		
		int InetSocket::get_ifce_address (
			const xp_char_t* name, int cmd, InetAddress* addr)
		{
			struct ifreq ifr;

			xp_memset (&ifr, 0, xp_sizeof(ifr));		
		#ifdef XP_CHAR_IS_MCHAR
			xp_strxcpy (ifr.ifr_name, xp_countof(ifr.ifr_name), name);
		#else
			if (xp_wcstomcs(name, ifr.ifr_name,
				xp_countof(ifr.ifr_name)) < xp_strlen(name)) return -1;
		#endif

			if (ioctl (this->sckhnd, cmd, &ifr) == -1) return -1;

			struct sockaddr* sa = (struct sockaddr*)&ifr.ifr_addr;
			if (sa->sa_family == InetAddress::V4) {
				*addr = InetAddress (*(struct sockaddr_in*)sa);
			}
			else if (sa->sa_family == InetAddress::V6) {
				*addr = InetAddress (*(struct sockaddr_in6*)sa);
			}
			else return -1;

			return 0;	
		}
#endif

	}
}
